package com.apes.oap.request.converter;


import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 功能：
 *
 * @author xul
 * @create 2018-09-05 10:24
 */
public class FormMessageConverter implements HttpMessageConverter<Map<String, ?>> {

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Charset charset = DEFAULT_CHARSET;

    private List<MediaType> supportedMediaTypes = new ArrayList<>();

    public FormMessageConverter() {
        this.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
    }


    /**
     * Set the default character set to use for reading and writing form data when
     * the request or response Content-Type header does not explicitly specify it.
     * <p>By default this is set to "UTF-8".
     */
    public void setCharset(Charset charset) {
        this.charset = charset;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Collections.unmodifiableList(this.supportedMediaTypes);
    }

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        if (!Map.class.isAssignableFrom(clazz)) {
            return false;
        }
        if (mediaType == null) {
            return true;
        }
        for (MediaType supportedMediaType : getSupportedMediaTypes()) {
            if (supportedMediaType.includes(mediaType)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public Map<String, String> read(Class<? extends Map<String, ?>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        MediaType contentType = inputMessage.getHeaders().getContentType();
        Charset charset = getCharSet(contentType);
        charset = (charset != null ? charset : this.charset);
        String body = StreamUtils.copyToString(inputMessage.getBody(), charset);

        String[] pairs = StringUtils.tokenizeToStringArray(body, "&");
        Map<String, String> result = new HashMap<>(pairs.length);
        for (String pair : pairs) {
            int idx = pair.indexOf('=');
            if (idx == -1) {
                result.put(URLDecoder.decode(pair, charset.name()), null);
            } else {
                String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
                String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
                result.put(name, value);
            }
        }
        return result;
    }

    @Override
    public void write(Map<String, ?> map, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
    }

    private Charset getCharSet(MediaType contentType) {
        String charSet = contentType.getParameter("charset");
        return charSet != null ? Charset.forName(this.unquote(charSet)) : null;
    }

    private String unquote(String s) {
        if (s == null) {
            return null;
        } else {
            return this.isQuotedString(s) ? s.substring(1, s.length() - 1) : s;
        }
    }

    private boolean isQuotedString(String s) {
        if (s.length() < 2) {
            return false;
        } else {
            return s.startsWith("\"") && s.endsWith("\"") || s.startsWith("'") && s.endsWith("'");
        }
    }

}
